home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / utils / console / svgatext.3 / svgatext / SVGATextMode-1.3 / ttyresize.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-03-31  |  13.1 KB  |  464 lines

  1. /*  SVGATextMode -- An SVGA textmode manipulation/enhancement tool
  2.  *
  3.  *  Copyright (C) 1995,1996  Koen Gadeyne
  4.  *
  5.  *  This program is free software; you can redistribute it and/or modify
  6.  *  it under the terms of the GNU General Public License as published by
  7.  *  the Free Software Foundation; either version 2 of the License, or
  8.  *  (at your option) any later version.
  9.  *
  10.  *  This program is distributed in the hope that it will be useful,
  11.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  *  GNU General Public License for more details.
  14.  *
  15.  *  You should have received a copy of the GNU General Public License
  16.  *  along with this program; if not, write to the Free Software
  17.  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  */
  19.  
  20.  
  21. /***
  22.  *** Linux tty resizing code for SVGATextMode.
  23.  ***
  24.  *** DOS resizing code for SVGATextMode.
  25.  ***    (c) 1995 Stephen Lee
  26.  ***/
  27.  
  28. #define USE_MMAP  0   /* 1: use mmap() ; 0: use malloc() */
  29.  
  30. #ifndef DOS
  31.  
  32. #include <linux/version.h>
  33. #if LINUX_VERSION_CODE < 66382
  34. #  define __KERNEL__
  35. #  include <linux/tty.h>    /* for MAX_NR_CONSOLES  -- __KERNEL__ is defined to get access to this parameter */
  36. #  undef __KERNEL__
  37. #else
  38. #  include <linux/tty.h>    /* for MAX_NR_CONSOLES */
  39. #endif
  40.  
  41. #include <stdio.h>
  42. #include <unistd.h>
  43. #include <stdlib.h>
  44. #include <string.h>
  45. #include <asm/page.h>    /* for PAGE_SIZE */
  46. #include <linux/vt.h>   /* for VT_RESIZE */
  47. #include <sys/utsname.h>
  48. #include <sys/ioctl.h>
  49. #include <sys/wait.h>
  50. #include <termios.h>
  51. #include <fcntl.h>
  52.  
  53. #include "misc.h"
  54. #include "messages.h"
  55. #include "cfg_structs.h"
  56. #include "ttyresize.h"
  57.  
  58. #if USE_MMAP
  59. #  include <sys/types.h>
  60. #  include <linux/fs.h>
  61. #  include <sys/mman.h>
  62. #endif
  63.  
  64. int opentty(char *devname)
  65. {
  66.   int fd;
  67.  
  68.   fd = open(devname, O_WRONLY | O_NOCTTY);
  69.   if (fd < 0)
  70.   {
  71.      perror("open");
  72.      PERROR(("Could not open %s for writing\n", devname));
  73.      return -1;
  74.   }
  75.   return(fd);
  76. }
  77.  
  78. void get_ttysize(int fd, char *devname, struct winsize *this_winsize)
  79. {
  80.   if (ioctl(fd, TIOCGWINSZ, this_winsize))
  81.   {
  82.      close(fd);
  83.      perror("TIOCGWINSZ");
  84.      PERROR(("Could not read Terminal size for %s\n", devname));
  85.   } 
  86. }
  87.  
  88. #ifndef NO_RESIZE
  89.  
  90. /*
  91.  * Check if required operation is supported by current kernel version.
  92.  */
  93.  
  94. int check_kernel_version(int req_ver, int req_rel, int req_pl, char* reason)
  95. {
  96.   struct utsname my_utsname;
  97.   int kversion, kv_ver, kv_rel, kv_pl;
  98.   int req_version = req_ver*1000000+req_rel*1000+req_pl;
  99.  
  100.   if (uname(&my_utsname))
  101.   {
  102.     perror("uname");
  103.     PWARNING(("Could not get kernel version number. Assuming worst case (<1.1.54) ...\n"));
  104.     return(FALSE);
  105.   }
  106.   
  107.   PDEBUG(("Checking if kernel supports %s (required: %d.%d.%d ; this: %s)\n",
  108.            reason, req_ver, req_rel, req_pl, my_utsname.release)); 
  109.   sscanf(my_utsname.release, "%d.%d.%d", &kv_ver, &kv_rel, &kv_pl);
  110.   kversion = kv_ver*1000000+kv_rel*1000+kv_pl;
  111.   return(kversion >= req_version);
  112. }
  113.  
  114. void set_ttysize(int fd, char *devname, struct winsize *this_winsize, int cols, int rows)
  115. {
  116.   /* no need to skip this if the screen is not resized. The kernel already does that */
  117.  
  118.   PDEBUG(("set_tysize: Resizing %s from %dx%d to %dx%d\n", devname, this_winsize->ws_col, this_winsize->ws_row, cols, rows));
  119.   this_winsize->ws_col = cols;
  120.   this_winsize->ws_row = rows;
  121.   if (ioctl(fd, TIOCSWINSZ, this_winsize))
  122.   {
  123.      close(fd);
  124.      perror("TIOCSWINSZ");
  125.      PERROR(("Could not set Terminal size for %s\n", devname));
  126.   }
  127. }
  128.  
  129. void resizetty(char *devicename, int cols, int rows)
  130. {
  131.   struct winsize my_winsize;
  132.   int fd;
  133.  
  134.   fd = opentty(devicename);
  135.   get_ttysize(fd, devicename, &my_winsize);
  136.   set_ttysize(fd, devicename, &my_winsize, cols, rows);
  137.   close(fd);
  138. }
  139.  
  140. /*
  141.  * Make real RAM free, by allocating and then releasing it.
  142.  */
  143.  
  144. int make_ram_free(size_t bytes)
  145. {
  146.   /*
  147.    * 1. Linux has a "lazy" malloc().  Memory is not actually allocated
  148.    * until it is used.  Conclusion: We must use the RAM to actually
  149.    * get it.
  150.    *
  151.    * 2. Linux has a "lazy" free().  Memory is not (necessarily) actually
  152.    * freed until the program exits.  Conclusion: We must fork off a
  153.    * child so that it can exit.
  154.    *
  155.    * Written by Michael Shields <shields@tembel.org>.
  156.    */
  157.  
  158. #if USE_MMAP
  159.   caddr_t mm;
  160. #else
  161.   pid_t pid;
  162.   int status;
  163.   char *p;
  164.   size_t i;
  165. #endif
  166.  
  167.   if (!bytes)
  168.     return 0;
  169.     
  170.   PDEBUG(("Freeing %d bytes of RAM for VT_RESIZE (using %s())\n", bytes, USE_MMAP==1 ? "mmap" : "malloc"));
  171.  
  172. #if USE_MMAP
  173.   mm = mmap(0, bytes, PROT_NONE, MAP_PRIVATE|MAP_ANON, -1, 0);
  174.   if ( (int)mm == -1 )
  175.   {
  176.     PDEBUG(("Not enough memory for VT_RESIZE. Attempting to free some disk buffers...\n"));
  177.     shrink_buffers(0);
  178.     mm = mmap(0, bytes, PROT_NONE, MAP_PRIVATE|MAP_ANON, -1, 0);
  179.     if ( (int)mm == -1 )
  180.     {
  181.       perror("mmap()");
  182.       return(-1);
  183.     }
  184.   }
  185.   munmap(mm, bytes); 
  186.   return(0);
  187. #else
  188.   pid = fork();
  189.  
  190.   switch(pid) {
  191.   case -1:
  192.     return -1;
  193.  
  194.   case 0:
  195.     p = (char *) malloc(bytes);
  196.     if (!p)
  197.       exit(1);
  198.  
  199.     /* Dirty the pages.  This is better than calloc(). */
  200.     for (i = 0; i < bytes; i += PAGE_SIZE)
  201.         p[i] = 1;
  202.  
  203.     free(p);
  204.     exit(0);
  205.  
  206.   default:
  207.     waitpid(pid, &status, 0);
  208.     return (WIFEXITED(status) && !WEXITSTATUS(status)) ? 0 : -1;
  209.   }
  210. #endif  
  211. }
  212.  
  213.  
  214. /*
  215.  * VT_RESIZE lets the KERNEL know that the screen has been resized,
  216.  * so it writes the chars in the correct place in video memory
  217.  *
  218.  * This is done BEFORE programming any hardware, so if it gives an "out of memory" error on heavily loaded machines,
  219.  * If it exits with an "ioctl: invalid argument" (because the kernel doesn't support VT_RESIZE),
  220.  * the screen isn't left all messed up. (suggested by Alessandro Rubini).
  221.  *
  222.  * NOTE: VT_RESIZE only sets the kernel parameters for correctly writing into the VGA memory. It does NOT resize
  223.  * terminals (the stty stuff). So it only makes the HARDWARE behave correctly. Terminals are another story.
  224.  *
  225.  */
  226.  
  227.  
  228. bool try_resize(int fd, void* p_struct_size, int memsize, int cmd)
  229. {
  230.   int cnt;
  231.  
  232.   /* The Real Thing (TM) ... */
  233.   if (!ioctl(fd, cmd, p_struct_size)) return(TRUE);   /* everything went OK */
  234.  
  235.   /* do a few attempts to get enough memory for VT_RESIZE, each time more, hoping we'll get it in the end */
  236.   for (cnt=1; cnt<=4; cnt++)
  237.   {
  238.     PDEBUG(("VT_RESIZE: Could not get memory. Trying to free some (%d), and attempting again...\n", memsize*cnt));
  239.     if (make_ram_free(memsize*cnt))
  240.       PERROR(("malloc(): Could not get %d bytes of memory.\n"
  241.               "  Close some applications and try again, or add some swap space...\n", memsize*cnt));
  242.  
  243.     /* The Real Thing (TM) ... */
  244.     if (!ioctl(fd, cmd, p_struct_size)) return(TRUE);   /* everything went OK */
  245.   }
  246.   return(FALSE);
  247. }
  248.  
  249. bool generic_VT_RESIZE(void* p_struct_size, void* dummy, int allow1x1, int memsize, int cmd, char* descr)
  250. {
  251.   int fd;
  252.  
  253.   PDEBUG(("%s\n", descr));
  254.   fd = opentty("/dev/console");
  255.  
  256.   if (try_resize(fd, p_struct_size, memsize, cmd)) return(FALSE);
  257.  
  258.   /* still no luck... if the error is not "out of memory", we don't know what to do and abort. */
  259.   perror(descr); if (errno!=ENOMEM) PERROR(("VT_RESIZE returned error %d\n", errno));
  260.  
  261.   /* if we are not allowed to resize to a 1x1 screen, we have no more options but to abort */
  262.   if (!allow1x1)
  263.   {
  264.     PERROR(("Not enough free _physical_ RAM to resize the screen.\n"
  265.      "  SVGATextMode needs a fairly large block of _contiguous_ memory.\n"
  266.      "  You might have enough free memory, but then it's fragmented too much.\n"
  267.      "  Consider using the '-m' command line switch to clear more memory,\n"
  268.      "  BUT this clears ALL screens in the process! (Read the manual first!)\n"));
  269.   }
  270.  
  271.   /* Get more memory by temporarily resizing the screen to 1x1, and THEN back to the new size */
  272.   PMESSAGE(("Not enough free RAM. Trying via 1x1 screen...\n"));
  273.   if (ioctl(fd, cmd, dummy))
  274.   {
  275.       /* This is NOT good. We're in trouble */ 
  276.       perror(descr);
  277.       PERROR(("Could not even resize screen to 1x1 to free more RAM.\n"
  278.        "  You REALLY need to free some memory.\n"));
  279.   }
  280.   
  281.   /* now try again, but first reclaim the RAM. It could have been snatched away again. */
  282.   if (try_resize(fd, p_struct_size, memsize, cmd))
  283.   {
  284.     PWARNING(("Could not get enough RAM for %s.\n"
  285.      "  Tried via 1x1 screen to get even more memory.\n"
  286.      "  All screens will be erased, except those that redraw themselves.\n", descr));
  287.     return(TRUE);  /* "TRUE" means we went via a 1x1 screen to succesfully resize the screen */
  288.   }
  289.  
  290.   /* At this point, we're in serious trouble... */
  291.   PWARNING(("Could not set kernel screen size parameters.\n"
  292.      "  Serious trouble! (you are probably left with a 1x1 screen...)\n"));
  293.  
  294.   return(TRUE);
  295.  
  296. }
  297.  
  298. int do_VT_RESIZE(int cols, int rows, int allow1x1)
  299. {
  300.   struct vt_sizes my_vt_size, dummy_vt_size;      /* passes the new screen size on to the kernel */
  301.  
  302.   /* We need two bytes for each character (character + attribute), per console. */
  303.   int ram_needed = cols * rows * 2 * MAX_NR_CONSOLES;
  304.  
  305.   my_vt_size.v_rows = rows;
  306.   my_vt_size.v_cols = cols;
  307.   my_vt_size.v_scrollsize = 0; /* kernel tries to get as many scroll-back lines as possible by itself (?) */
  308.   
  309.   dummy_vt_size.v_rows = 1;
  310.   dummy_vt_size.v_cols = 1;
  311.   dummy_vt_size.v_scrollsize = 0;
  312.  
  313.   return(generic_VT_RESIZE(&my_vt_size, &dummy_vt_size, allow1x1, ram_needed, VT_RESIZE, "VT_RESIZE"));
  314. }
  315.  
  316. /*
  317.  * if VT_RESIZEX not supported (i.e. when compiling on < 1.3.3 kernels), define it.
  318.  * this is just te keep the compiler happy
  319.  */
  320.  
  321. #ifndef VT_RESIZEX
  322. #  define VT_RESIZEX  0x560A
  323.    typedef struct vt_consize { 
  324.       ushort v_rows; ushort v_cols; ushort v_vlin; ushort v_clin; ushort v_vcol; ushort v_ccol;
  325.     } vt_consize;
  326. #endif
  327.  
  328.  
  329. int do_VT_RESIZEX(int cols, int rows, int vlin, int clin, int vcol, int ccol, int allow1x1)
  330. {
  331.   struct vt_consize my_vt_size;      /* passes the new screen size on to the kernel */
  332.   struct vt_consize dummy_vt_size = { 1 , 1 , 1 , 1 , 1 , 1 };
  333.   int ram_needed = cols * rows * 2 * MAX_NR_CONSOLES;
  334.  
  335.   my_vt_size.v_rows = rows;
  336.   my_vt_size.v_cols = cols;
  337.   my_vt_size.v_vlin = vlin;
  338.   my_vt_size.v_clin = clin;
  339.   my_vt_size.v_vcol = vcol;
  340.   my_vt_size.v_ccol = ccol;
  341.  
  342.   PDEBUG(("VT_RESIZEX(cols=%d,rows=%d,vlin=%d,clin=%d,vcol=%d,ccol=%d)\n",cols, rows, vlin, clin, vcol, ccol));
  343.   
  344.   return(generic_VT_RESIZE(&my_vt_size, &dummy_vt_size, allow1x1, ram_needed, VT_RESIZEX, "VT_RESIZEX"));
  345. }
  346.  
  347. /*
  348.  * resize all specified VT's.
  349.  *
  350.  * This function resizes the TERMINALS (equivalent to "stty rows ... cols ...) , if any were given in a "Terminals" line .
  351.  * Another fine suggestion by Kenneth Albanowski
  352.  *
  353.  * It complements VT_RESIZE, which only resizes "the hardware". This resizes "the software".
  354.  */
  355.   
  356. void resize_specified_vts(int cols, int rows)
  357. {
  358.   t_terminals *curr_term;
  359.  
  360.   PDEBUG(("Resizing all terminals specified in 'Terminals' line (when needed)\n"));
  361.   
  362.   curr_term = p_terminals;
  363.   while (curr_term) resizetty(curr_term->name, cols, rows);
  364. }
  365.  
  366.  
  367. /*
  368.  * Resize the VT's reported active from VT_GETSTATE, unfortunately
  369.  * the return param is a short so we can only hope to know about the
  370.  * first 16.  This won't do anything to VT's beyond those first 16.
  371.  * Returns 0 on success and -1 on failure.
  372.  *
  373.  * If you have >16 VT's to resize, you'll have to use the "Terminals" line.
  374.  *
  375.  * Written by Reuben Sumner <rasumner@undergrad.math.uwaterloo.ca>
  376.  * and adapted a little by kmg... (sorry, I couldn't resist)
  377.  */
  378.  
  379. void resize_active_vts(int cols, int rows)
  380. {
  381.    int fd;
  382.    struct vt_stat vst;
  383.    unsigned short mask;
  384.    int i;
  385.    char devicename[64];
  386.    
  387.    PDEBUG(("Resizing all active VT's when needed\n"));
  388.    
  389.    fd = opentty("/dev/console");
  390.    if (ioctl(fd, VT_GETSTATE, &vst))
  391.    {
  392.       perror("VT_GETSTATE");
  393.       PERROR(("Could not do VT_GETSTATE on /dev/console\n"));
  394.    }
  395.    close(fd);
  396.  
  397.   /* vst.v_state is a 16-bit "short": bit 0 = tty0, bit 1 = tty1 , ..., bit 15 = tty15
  398.    * tty0 is a special case: it is the _current_ console (also /dev/console)
  399.    * /dev/tty0 does not exist on some systems, plus it is also the current console (e.g. 
  400.    * /dev/tty3), so we don't resize it, avoiding an error on some systems, and also avoiding
  401.    * resizing the same tty twice.
  402.    */
  403.     
  404.    for (mask = 2, i = 1; i < 16; i++, mask <<= 1)
  405.    {
  406.       if ((vst.v_state & mask) != 0)       /* only resize active VT's */
  407.       {
  408.         sprintf(devicename,"/dev/tty%d",i);
  409.         resizetty(devicename, cols, rows);
  410.       }
  411.    }
  412. }
  413.  
  414. #endif /* NO_RESIZE */
  415.  
  416. int check_if_resize(int cols, int rows)
  417. {
  418.   struct winsize my_winsize;
  419.   int fd;
  420.   char devicename[]="/dev/console";
  421.   
  422.   PDEBUG(("Checking if new mode requires screen resizing (from %s)\n", devicename));
  423.  
  424.   fd = opentty(devicename);
  425.   get_ttysize(fd, devicename, &my_winsize);
  426.   close(fd);
  427.   return((my_winsize.ws_col != cols) || (my_winsize.ws_row != rows));
  428. }
  429.  
  430.  
  431. #else
  432.  
  433. /***
  434.  *** DOS resizing code for SVGATextMode.
  435.  *** (c) 1995 Stephen Lee
  436.  ***/
  437.  
  438. /* for DJGPP v2 */
  439. #include <pc.h>
  440. #include <go32.h>
  441. #include <sys/farptr.h>
  442.  
  443. #include "messages.h"
  444.  
  445. int resize_DOS(int cols, int rows)
  446. {
  447.   unsigned bioscolsp = 0x0044a;
  448.   unsigned biosrowsp = 0x00484;
  449.   
  450.   _farpokew(_dos_ds, bioscolsp, cols);
  451.   _farpokeb(_dos_ds, biosrowsp, rows - 1);
  452.     
  453.   return 0;
  454. }
  455.  
  456. int check_if_resize(int cols, int rows)
  457. {
  458.   PDEBUG(("Checking if new mode requires screen resizing (from BIOS)\n"));
  459.  
  460.   return((ScreenCols() != cols) || (ScreenRows() != rows));
  461. }
  462.  
  463. #endif
  464.